// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include <zencore/thread.h>
#include <zencore/zencore.h>

#include <filesystem>

namespace zen {

/** Basic process abstraction
 */
class ProcessHandle
{
public:
	ZENCORE_API ProcessHandle();

	ProcessHandle(const ProcessHandle&) = delete;
	ProcessHandle& operator=(const ProcessHandle&) = delete;

	ZENCORE_API ~ProcessHandle();

	ZENCORE_API void			   Initialize(int Pid);
	ZENCORE_API void			   Initialize(int Pid, std::error_code& OutEc);
	ZENCORE_API void			   Initialize(void* ProcessHandle);	 /// Initialize with an existing handle - takes ownership of the handle
	ZENCORE_API [[nodiscard]] bool IsRunning() const;
	ZENCORE_API [[nodiscard]] bool IsValid() const;
	ZENCORE_API bool			   Wait(int TimeoutMs = -1);
	ZENCORE_API int				   WaitExitCode();
	ZENCORE_API int				   GetExitCode();
	ZENCORE_API bool			   Terminate(int ExitCode);
	ZENCORE_API void			   Reset();
	[[nodiscard]] inline int	   Pid() const { return m_Pid; }

private:
	void* m_ProcessHandle = nullptr;
	int	  m_Pid			  = 0;
#if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC
	int m_ExitCode = -1;
#endif
};

/** Basic process creation
 */
struct CreateProcOptions
{
	enum
	{
		Flag_NewConsole = 1 << 0,
		Flag_Elevated	= 1 << 1,
		Flag_Unelevated = 1 << 2,
	};

	const std::filesystem::path* WorkingDirectory = nullptr;
	uint32_t					 Flags			  = 0;
	std::filesystem::path		 StdoutFile;
};

#if ZEN_PLATFORM_WINDOWS
using CreateProcResult = void*;	 // handle to the process
#else
using CreateProcResult = int32_t;  // pid
#endif

ZENCORE_API CreateProcResult CreateProc(const std::filesystem::path& Executable,
										std::string_view			 CommandLine,  // should also include arg[0] (executable name)
										const CreateProcOptions&	 Options = {});

/** Process monitor - monitors a list of running processes via polling

	Intended to be used to monitor a set of "sponsor" processes, where
	we need to determine when none of them remain alive

 */

class ProcessMonitor
{
public:
	ProcessMonitor();
	~ProcessMonitor();

	ZENCORE_API bool IsRunning();
	ZENCORE_API void AddPid(int Pid);
	ZENCORE_API bool IsActive() const;

private:
	using HandleType = void*;

	mutable RwLock			m_Lock;
	std::vector<HandleType> m_ProcessHandles;
};

ZENCORE_API bool IsProcessRunning(int pid);
ZENCORE_API bool IsProcessRunning(int pid, std::error_code& OutEc);
ZENCORE_API int	 GetCurrentProcessId();
int				 GetProcessId(CreateProcResult ProcId);

std::filesystem::path GetProcessExecutablePath(int Pid, std::error_code& OutEc);
std::error_code		  FindProcess(const std::filesystem::path& ExecutableImage, ProcessHandle& OutHandle);

void process_forcelink();  // internal

}  // namespace zen
